using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.IO;
using System.Reflection;
using System.Runtime.Serialization;
using System.Text;
using System.Windows.Forms;
using System.Xml;
using System.Xml.Schema;
using System.Xml.Serialization;
using OpenComposite.Properties;
using OpenComposite.Base;

namespace OpenComposite.EII.Designer.SCA
{
	[ToolboxItem(false)]
	public partial class UCTreeConnector : UserControl
	{
		#region Initialization

		public UCTreeConnector()
		{
			InitializeComponent();

			init();
		}

		private void init()
		{
			treeRight.MouseWheel += new MouseEventHandler(tree_MouseWheel);
			treeLeft.MouseWheel += new MouseEventHandler(tree_MouseWheel);

			treeLeft.ExpandAll();
			treeRight.ExpandAll();

			_lstSelectedConnections = new List<ConnectionItem>();
			_lstConnections = new List<ConnectionItem>();
		}

		#endregion

		#region Public Members

		#region Properties

		public TreeView LeftTreeView
		{
			get { return treeLeft; }
		}
		public TreeView RightTreeView
		{
			get { return treeRight; }
		}

		public List<ConnectionItem> Connections
		{
			get { return _lstConnections; }
		}
		public List<ConnectionItem> SelectedConnections
		{
			get { return _lstSelectedConnections; }
		}

		public override ContextMenuStrip ContextMenuStrip
		{
			get { return contextAssignment; }
			set {
				if ( contextAssignment != value ) {
					contextAssignment.Dispose();
					contextAssignment = null;
				}
				contextAssignment = value;
			}
		}

		#endregion

		#region Events
		public event EventHandler<ConnectingEventArgs> Connecting = null;
		public event EventHandler<ConnectedEventArgs> Connected = null;

		public event EventHandler<ConnectionItemsEventArgs> ConnectionsRemoved = null;
		#endregion

		#region Methods
		public void SetNodeTag(TreeNode node, object tag)
		{
			if ( node == null )
				throw new ArgumentNullException("node");

			if ( node.Tag is TreeNodeInfos )
				( (TreeNodeInfos)node.Tag ).Tag = tag;
			else {
				TreeNodeInfos tni = new TreeNodeInfos();
				tni.Tag = tag;
				node.Tag = tni;
			}
		}
		public object GetNodeTag(TreeNode node)
		{
			object tag = null;
			if ( node.Tag is TreeNodeInfos )
				tag = ( (TreeNodeInfos)node.Tag ).Tag;
			else
				tag = node.Tag;
			return tag;
		}

		public XmlDocument GetConnectionXml()
		{
			return serializeConnectionXml();
		}
		public void SetConnectionXml(XmlDocument xml)
		{
			deserializeConnectionXml(xml);
		}

		public void AddConnection(TreeNode tnLeft, TreeNode tnRight, ConnectDirection direction)
		{
			this.setNewConnection(tnLeft, tnRight, direction);
		}
		#endregion

		#endregion

		#region Protected Members

		#region Methods

		protected virtual void OnConnecting(ConnectingEventArgs e)
		{
			EventHandler<ConnectingEventArgs> tmp = this.Connecting;
			if ( tmp != null )
				tmp(this, e);
		}
		protected virtual void OnConnected(ConnectedEventArgs e)
		{
			EventHandler<ConnectedEventArgs> tmp = this.Connected;
			if ( tmp != null )
				tmp(this, e);
		}

		protected virtual void OnConnectionsRemoved(ConnectionItemsEventArgs e)
		{
			EventHandler<ConnectionItemsEventArgs> tmp = this.ConnectionsRemoved;
			if ( tmp != null )
				tmp(this, e);
		}
		#endregion

		#endregion

		#region Private Members

		#region Variables

		private List<ConnectionItem> _lstConnections;
		private List<ConnectionItem> _lstSelectedConnections;

		private bool _bIsInAfterSelect = false;

		#endregion

		#region Event Handlers

		private void tableLayoutMapping_CellPaint(object sender, TableLayoutCellPaintEventArgs e)
		{
			if ( e.Column != 1 )
				return;

			foreach ( ConnectionItem mi in _lstConnections ) {
				Point ptStart, ptEnd;
				if ( mi.LeftTreeNode.IsVisible )
					ptStart = new Point(e.CellBounds.Left, mi.LeftTreeNode.Bounds.Top + mi.LeftTreeNode.Bounds.Height / 2);
				else {
					TreeNode tnParent = searchVisibleParent(mi.LeftTreeNode);
					ptStart = new Point(e.CellBounds.Left, tnParent.Bounds.Top + tnParent.Bounds.Height / 2);
				}

				if ( mi.RightTreeNode.IsVisible )
					ptEnd = new Point(e.CellBounds.Right, mi.RightTreeNode.Bounds.Top + mi.RightTreeNode.Bounds.Height / 2);
				else {
					TreeNode tnParent = searchVisibleParent(mi.RightTreeNode);
					ptEnd = new Point(e.CellBounds.Right, tnParent.Bounds.Top + tnParent.Bounds.Height / 2);
				}
				if ( ptStart.Y < e.CellBounds.Top ) ptStart.Y = e.CellBounds.Top + 1;
				if ( ptEnd.Y < e.CellBounds.Top ) ptEnd.Y = e.CellBounds.Top + 1;
				if ( ptStart.Y > e.CellBounds.Bottom ) ptStart.Y = e.CellBounds.Bottom - 2;
				if ( ptEnd.Y > e.CellBounds.Bottom ) ptEnd.Y = e.CellBounds.Bottom - 2;

				mi.ConnectionPath.Reset();
				mi.ConnectionPath.AddLine(ptStart, ptEnd);
				e.Graphics.SmoothingMode = SmoothingMode.HighQuality;
				if ( mi.LeftTreeNode.IsSelected && mi.RightTreeNode.IsSelected )
					e.Graphics.DrawPath(SystemPens.Highlight, mi.ConnectionPath);
				else
					e.Graphics.DrawPath(SystemPens.GrayText, mi.ConnectionPath);
				e.Graphics.SmoothingMode = SmoothingMode.Default;
			}
		}

		private void tableLayoutMapping_MouseClick(object sender, MouseEventArgs e)
		{
			using ( Pen penOutline = new Pen(Color.Black, 5) ) {
				foreach ( ConnectionItem mi in _lstConnections ) {
					if ( mi.ConnectionPath.IsOutlineVisible(e.X, e.Y, penOutline) ) {
						treeLeft.SelectedNode = mi.LeftTreeNode;
						treeRight.SelectedNode = mi.RightTreeNode;
						tableLayoutMapping.Invalidate();
						if ( ( Control.ModifierKeys & Keys.Control ) == 0 )
							_lstSelectedConnections.Clear();
						_lstSelectedConnections.Add(mi);
						if ( e.Button == MouseButtons.Right ) {
							contextAssignment.Show((Control)sender, e.X, e.Y);
						}
						break;
					}
				}
			}
		}

		#region Drag'n'Drop Left Tree

		private void treeLeft_ItemDrag(object sender, ItemDragEventArgs e)
		{
			treeLeft.DoDragDrop(e.Item, DragDropEffects.Link | DragDropEffects.Move);
		}
		private void treeLeft_DragDrop(object sender, DragEventArgs e)
		{
			TreeNode tnRight = e.Data.GetData(typeof(TreeNode)) as TreeNode;
			if ( tnRight == null || tnRight.TreeView != treeRight )
				return;

			TreeViewHitTestInfo hit = treeLeft.HitTest(treeLeft.PointToClient(new Point(e.X, e.Y)));
			if ( hit.Node == null )
				return;

			setNewConnection(hit.Node, tnRight, ConnectDirection.RightToLeft);
		}
		private void treeLeft_DragEnter(object sender, DragEventArgs e)
		{

		}
		private void treeLeft_DragLeave(object sender, EventArgs e)
		{

		}
		private void treeLeft_DragOver(object sender, DragEventArgs e)
		{
			TreeNode tn = e.Data.GetData(typeof(TreeNode)) as TreeNode;
			if ( tn == null || tn.TreeView == treeLeft )
				e.Effect = DragDropEffects.Move;
			else {
				TreeViewHitTestInfo hit = treeLeft.HitTest(treeLeft.PointToClient(new Point(e.X, e.Y)));
				if ( hit.Node != null ) {
					e.Effect = DragDropEffects.Link;
					treeLeft.SelectedNode = hit.Node;
				} else {
					e.Effect = DragDropEffects.Move;
				}
			}
		}
		private void treeLeft_GiveFeedback(object sender, GiveFeedbackEventArgs e)
		{

		}
		private void treeLeft_QueryContinueDrag(object sender, QueryContinueDragEventArgs e)
		{

		}

		#endregion

		#region Drag'n'Drop Right Tree

		private void treeRight_ItemDrag(object sender, ItemDragEventArgs e)
		{
			treeLeft.DoDragDrop(e.Item, DragDropEffects.Link | DragDropEffects.Move);
		}
		private void treeRight_DragDrop(object sender, DragEventArgs e)
		{
			TreeNode tnLeft = e.Data.GetData(typeof(TreeNode)) as TreeNode;
			if ( tnLeft == null || tnLeft.TreeView != treeLeft )
				return;

			TreeViewHitTestInfo hit = treeRight.HitTest(treeRight.PointToClient(new Point(e.X, e.Y)));
			if ( hit.Node == null )
				return;

			setNewConnection(tnLeft, hit.Node, ConnectDirection.LeftToRight);
		}
		private void treeRight_DragEnter(object sender, DragEventArgs e)
		{

		}
		private void treeRight_DragLeave(object sender, EventArgs e)
		{

		}
		private void treeRight_DragOver(object sender, DragEventArgs e)
		{
			TreeNode tn = e.Data.GetData(typeof(TreeNode)) as TreeNode;
			if ( tn == null || tn.TreeView == treeRight )
				e.Effect = DragDropEffects.Move;
			else {
				TreeViewHitTestInfo hit = treeRight.HitTest(treeRight.PointToClient(new Point(e.X, e.Y)));
				if ( hit.Node != null ) {
					e.Effect = DragDropEffects.Link;
					treeRight.SelectedNode = hit.Node;
				} else {
					e.Effect = DragDropEffects.Move;
				}
			}
		}
		private void treeRight_GiveFeedback(object sender, GiveFeedbackEventArgs e)
		{

		}
		private void treeRight_QueryContinueDrag(object sender, QueryContinueDragEventArgs e)
		{

		}

		#endregion

		private void treeLeft_DrawNode(object sender, DrawTreeNodeEventArgs e)
		{
			drawTreeNode((TreeView)sender, e);
		}
		private void treeRight_DrawNode(object sender, DrawTreeNodeEventArgs e)
		{
			drawTreeNode((TreeView)sender, e);
		}

		private void treeLeft_Scroll(object sender, ScrollEventArgs e)
		{
			if ( e.ScrollOrientation == ScrollOrientation.VerticalScroll ) {
				tableLayoutMapping.Invalidate();
			}
		}
		private void treeRight_Scroll(object sender, ScrollEventArgs e)
		{
			if ( e.ScrollOrientation == ScrollOrientation.VerticalScroll ) {
				tableLayoutMapping.Invalidate();
			}
		}

		private void tree_AfterCollapse(object sender, TreeViewEventArgs e)
		{
			tableLayoutMapping.Invalidate();
		}
		private void tree_AfterExpand(object sender, TreeViewEventArgs e)
		{
			tableLayoutMapping.Invalidate();
		}

		private void tree_MouseWheel(object sender, MouseEventArgs e)
		{
			tableLayoutMapping.Invalidate();
		}

		private void tree_AfterSelect(object sender, TreeViewEventArgs e)
		{
			if ( _bIsInAfterSelect )
				return;

			try {
				_bIsInAfterSelect = true;
				TreeNodeInfos tni = e.Node.Tag as TreeNodeInfos;
				if ( tni == null || tni.Connections.Count == 0 ) {
					_lstSelectedConnections.Clear();
					if ( sender == treeLeft )
						treeRight.SelectedNode = null;
					if ( sender == treeRight )
						treeLeft.SelectedNode = null;
					tableLayoutMapping.Invalidate();
					return;
				}
				_lstSelectedConnections.Clear();
				_lstSelectedConnections.AddRange(tni.Connections);

				if ( sender == treeLeft )
					treeRight.SelectedNode = tni.Connections[0].RightTreeNode;
				if ( sender == treeRight )
					treeLeft.SelectedNode = tni.Connections[0].LeftTreeNode;

				tableLayoutMapping.Invalidate();
			} catch ( Exception ex ) {
				Debug.WriteLine(ex.ToString());
			} finally {
				_bIsInAfterSelect = false;
			}
		}

		private void SchemaValidationCallback(object sender, ValidationEventArgs e)
		{
			MessageBox.Show(e.Message);
		}
		
		private void miDeleteAssignment_Click(object sender, EventArgs e)
		{
			// precondition
			if ( _lstSelectedConnections.Count == 0 )
				return;

			// remove selected connections
			TreeNodeInfos tni;
			List<ConnectionItem> listRemoved = new List<ConnectionItem>();

			foreach ( ConnectionItem ci in _lstSelectedConnections ) {
				tni = ci.LeftTreeNode.Tag as TreeNodeInfos;
				if ( tni != null ) tni.Connections.Remove(ci);
				tni = ci.RightTreeNode.Tag as TreeNodeInfos;
				if ( tni != null ) tni.Connections.Remove(ci);
				listRemoved.Add(ci);
				_lstConnections.Remove(ci);
			}
			_lstSelectedConnections.Clear();
			treeLeft.SelectedNode = null;
			treeRight.SelectedNode = null;
			tableLayoutMapping.Invalidate();

			// send ConnectionsRemoved event
			if ( listRemoved.Count > 0 )
				OnConnectionsRemoved(new ConnectionItemsEventArgs(listRemoved));
		}

		#endregion

		#region Methods

		/// <summary>
		/// Draw the TreeNode
		/// </summary>
		/// <param name="tv">TreeView</param>
		/// <param name="e">DrawTreeNodeEventArgs</param>
		private void drawTreeNode(TreeView tv, DrawTreeNodeEventArgs e)
		{
			Rectangle focusBounds = nodeBounds(tv, e.Node);
			focusBounds.Location = new Point(focusBounds.Left, focusBounds.Top);
			focusBounds.Size = new Size(focusBounds.Width, focusBounds.Height - 1);

			Rectangle rectClear = new Rectangle(e.Bounds.X, e.Bounds.Y, e.Bounds.Width + 1, e.Bounds.Height);
			e.Graphics.FillRectangle(SystemBrushes.Window, rectClear);

			// Draw the background and node text for a selected node.
			if ( ( e.State & TreeNodeStates.Selected ) != 0 ) {
				// Draw the background of the selected node. The NodeBounds
				// method makes the highlight rectangle large enough to
				// include the text of a node tag, if one is present.
				e.Graphics.FillRectangle(SystemBrushes.Highlight, focusBounds/*Rectangle.Inflate(focusBounds,-1,-1)*/);

				// Retrieve the node font. If the node font has not been set,
				// use the TreeView font.
				Font nodeFont = e.Node.NodeFont;
				if ( nodeFont == null ) nodeFont = tv.Font;

				// Draw the node text.
				Rectangle rectText = e.Node.Bounds;// Rectangle.Inflate(e.Node.Bounds, 2, 0);
				rectText.Width += 6;
				e.Graphics.DrawString(e.Node.Text, nodeFont, SystemBrushes.HighlightText, rectText);
			} else {
				// Retrieve the node font. If the node font has not been set,
				// use the TreeView font.
				Font nodeFont = e.Node.NodeFont;
				if ( nodeFont == null ) nodeFont = tv.Font;

				// Draw the node text.
				Rectangle rectText = e.Node.Bounds;// Rectangle.Inflate(e.Node.Bounds, 0, 0);
				rectText.Width += 10;
				e.Graphics.DrawString(e.Node.Text, nodeFont, SystemBrushes.ControlText, rectText);
			}

			// If a node tag is present, draw its string representation 
			// to the right of the label text.
			if ( e.Node.Tag != null ) {
				e.Graphics.DrawString(e.Node.Tag.ToString(), tv.Font,
					Brushes.Black, e.Node.Bounds.Right + 2, e.Bounds.Top);
			}

			// If the node has focus, draw the focus rectangle large, making
			// it large enough to include the text of the node tag, if present.
			if ( ( e.State & TreeNodeStates.Focused ) != 0 ) {
				using ( Pen focusPen = new Pen(Color.Black) ) {
					focusPen.DashStyle = System.Drawing.Drawing2D.DashStyle.Dot;
					focusBounds.Width--;
					focusBounds.Height--;
					e.Graphics.DrawRectangle(focusPen, focusBounds);
				}
			}
			// Draw the border and lines for the mapping
			if ( e.Node.Tag is TreeNodeInfos ) {
				TreeNodeInfos tni = e.Node.Tag as TreeNodeInfos;
				if ( tni.Connections.Count > 0 ) {
					using ( Pen pen = new Pen(e.Node.IsSelected ? SystemColors.Highlight : SystemColors.GrayText) ) {
						Rectangle rectMap = nodeBounds(tv, e.Node);
						rectMap.Width += rectMap.Left - 6;
						rectMap.Location = new Point(6, rectMap.Top);
						rectMap.Height -= 2;
						e.Graphics.DrawRectangle(pen, rectMap);

						Point ptStart, ptEnd;
						if ( tv == treeLeft ) {
							ptStart = new Point(rectMap.Right, e.Node.Bounds.Top + e.Node.Bounds.Height / 2);
							ptEnd = new Point(tv.Right, e.Node.Bounds.Top + e.Node.Bounds.Height / 2);
						} else {
							ptStart = new Point(0, e.Node.Bounds.Top + e.Node.Bounds.Height / 2);
							ptEnd = new Point(rectMap.Left, e.Node.Bounds.Top + e.Node.Bounds.Height / 2);
						}
						e.Graphics.DrawLine(pen, ptStart, ptEnd);
					}
				}
			}
		}

		/// <summary>
		/// Returns the bounds of the specified node, including the region
		/// occupied by the node label and any node tag displayed.
		/// </summary>
		private Rectangle nodeBounds(TreeView tv, TreeNode node)
		{
			// Set the return value to the normal node bounds.
			Rectangle bounds = node.Bounds;
			if ( node.Tag != null ) {
				// Retrieve a Graphics object from the TreeView handle
				// and use it to calculate the display width of the tag.
				using ( Graphics g = tv.CreateGraphics() ) {
					int tagWidth = (int)g.MeasureString
						(node.Tag.ToString(), tv.Font).Width + 6;

					// Adjust the node bounds using the calculated value.
					bounds.Offset(tagWidth / 2, 0);
					bounds = Rectangle.Inflate(bounds, tagWidth / 2, 0);
				}
			}
			return bounds;
		}

		private void drawParentLine(DrawTreeNodeEventArgs e, TreeNode current, Pen pen)
		{
			if ( current.Nodes.Count > 0 && current.IsExpanded ) {
				int x = current.Bounds.Left + current.TreeView.Indent / 2;
				Rectangle rectLastChild = current.Nodes[current.Nodes.Count - 1].Bounds;
				e.Graphics.DrawLine(pen, x, e.Node.Bounds.Bottom, x,
					rectLastChild.Top + rectLastChild.Height / 2);
			}
			if ( current.Parent != null )
				drawParentLine(e, current.Parent, pen);
		}

		private void setNewConnection(TreeNode tnLeft, TreeNode tnRight, ConnectDirection direction)
		{
			ConnectionItem ci = new ConnectionItem(tnLeft, tnRight, direction);
			ConnectionItem ciFind = _lstConnections.Find(delegate(ConnectionItem item)
			{
				return ( ci.LeftTreeNode == item.LeftTreeNode && ci.RightTreeNode == item.RightTreeNode );
			});
			if ( ciFind == null ) {
				ConnectingEventArgs connetingargs = new ConnectingEventArgs(ci);
				OnConnecting(connetingargs);

				if ( connetingargs.Cancel )
					return;

				setTreeNodeInfosConnection(tnLeft, ci);
				setTreeNodeInfosConnection(tnRight, ci);

				_lstConnections.Add(ci);

				treeLeft.SelectedNode = tnLeft;
				treeRight.SelectedNode = tnRight;

				OnConnected(new ConnectedEventArgs(ci));
				this.Refresh();
			}
		}
		private void setTreeNodeInfosConnection(TreeNode tn, ConnectionItem ci)
		{
			if ( tn.Tag is TreeNodeInfos )
				( (TreeNodeInfos)tn.Tag ).Connections.Add(ci);
			else
				tn.Tag = new TreeNodeInfos(ci);
		}

		private TreeNode searchVisibleParent(TreeNode treeNode)
		{
			if ( treeNode.Parent == null )
				return treeNode;
			if ( treeNode.Parent.IsVisible )
				return treeNode.Parent;
			return searchVisibleParent(treeNode.Parent);
		}

		private XmlDocument serializeConnectionXml()
		{
			XmlDocument xd = new XmlDocument();
			using ( StringWriter sw = new StringWriter() ) {
				XmlSerializer xs = new XmlSerializer(typeof(ConnectionItem[]));
				xs.Serialize(sw, _lstConnections.ToArray());
				xd.LoadXml(sw.ToString());
			}
			return xd;
		}
		private void deserializeConnectionXml(XmlDocument connectionxml)
		{
			if ( connectionxml == null )
				return;

			ConnectionItem[] loadedConnections = null;
			using ( StringReader sr = new StringReader(connectionxml.DocumentElement.OuterXml) ) {
				XmlSerializer xs = new XmlSerializer(typeof(ConnectionItem[]));
				loadedConnections = (ConnectionItem[])xs.Deserialize(sr);
			}
			if ( loadedConnections == null || loadedConnections.Length == 0 )
				return;

			List<TreeNode> lstLeft = getFlatTree(treeLeft);
			List<TreeNode> lstRight = getFlatTree(treeRight);

			foreach ( ConnectionItem ci in loadedConnections ) {
				foreach ( TreeNode tnL in lstLeft ) {
					if ( tnL.FullPath == ci.LeftTreePath ) {
						foreach ( TreeNode tnR in lstRight ) {
							if ( tnR.FullPath == ci.RightTreePath )
								setNewConnection(tnL, tnR, ConnectDirection.LeftToRight);
						}
					}
				}
			}
			this.Refresh();
		}
		private List<TreeNode> getFlatTree(TreeView tv)
		{
			int i = 0;
			List<TreeNode> lst = new List<TreeNode>();
			foreach ( TreeNode tn in tv.Nodes ) lst.Add(tn);
			while ( i < lst.Count ) {
				if ( lst[i].Nodes.Count > 0 )
					foreach ( TreeNode tn in lst[i].Nodes ) lst.Add(tn);
				i++;
			}
			return lst;
		}

		#endregion

		#endregion

	}

	#region Class: ConnectionItem
	[Serializable]
	public class ConnectionItem
	{
		public ConnectionItem()
		{
			ConnectionPath = new GraphicsPath();
		}
		public ConnectionItem(TreeNode leftnode, TreeNode rightnode)
			: this()
		{
			LeftTreeNode = leftnode;
			RightTreeNode = rightnode;
		}
		public ConnectionItem(TreeNode leftnode, TreeNode rightnode, ConnectDirection direction)
			: this(leftnode, rightnode)
		{
			_direction = direction;
		}

		[NonSerialized, XmlIgnore]
		public TreeNode LeftTreeNode = null;
		[NonSerialized, XmlIgnore]
		public TreeNode RightTreeNode = null;
		[NonSerialized, XmlIgnore]
		public GraphicsPath ConnectionPath = null;

		public string LeftTreePath
		{
			get
			{
				if ( LeftTreeNode != null )
					return LeftTreeNode.FullPath;
				else
					return _leftTreePath;
			}
			set { _leftTreePath = value; }
		}
		public string RightTreePath
		{
			get
			{
				if ( RightTreeNode != null )
					return RightTreeNode.FullPath;
				else
					return _rightTreePath;
			}
			set { _rightTreePath = value; }
		}
		public ConnectDirection Direction
		{
			get { return _direction; }
			set { _direction = value; }
		}

		private string _leftTreePath = "";
		private string _rightTreePath = "";
		private ConnectDirection _direction = ConnectDirection.LeftToRight;

	}
	#endregion

	#region Enum: ConnectDirection
	public enum ConnectDirection
	{
		LeftToRight,
		RightToLeft,
		Both
	}
	#endregion

	#region Class: TreeNodeInfos
	public class TreeNodeInfos
	{
		public TreeNodeInfos()
		{
			this._tag = null;
			this._connections = new List<ConnectionItem>();
		}
		public TreeNodeInfos(ConnectionItem connection)
			: this()
		{
			if ( connection != null )
				this.Connections.Add(connection);
		}

		private List<ConnectionItem> _connections;
		public List<ConnectionItem> Connections
		{
			get { return _connections; }
		}
		private object _tag;
		public object Tag
		{
			get { return _tag; }
			set { _tag = value; }
		}

		public override string ToString()
		{
			return "";
		}
	}
	#endregion

	#region Class: ConnectingEventArgs ; BaseClass: EventArgs
	public class ConnectingEventArgs : EventArgs
	{
		public ConnectingEventArgs(ConnectionItem item)
		{
			_item = item;
			_cancel = false;
		}

		private readonly ConnectionItem _item;
		public ConnectionItem Item
		{
			get { return _item; }
		}

		private bool _cancel;
		public bool Cancel
		{
			get { return _cancel; }
			set { _cancel = value; }
		}
	}
	#endregion
	#region Class: ConnectedEventArgs ; BaseClass: EventArgs
	public class ConnectedEventArgs : EventArgs
	{
		public ConnectedEventArgs(ConnectionItem item)
		{
			_item = item;
		}

		private readonly ConnectionItem _item;
		public ConnectionItem Item
		{
			get { return _item; }
		}
	}
	#endregion
	#region Class: ConnectionItemsEventArgs ; BaseClass: EventArgs
	public class ConnectionItemsEventArgs : EventArgs
	{
		public ConnectionItemsEventArgs(List<ConnectionItem> items)
		{
			_items = items;
		}

		private readonly List<ConnectionItem> _items;
		public List<ConnectionItem> Items
		{
			get { return _items; }
		}
	}
	#endregion
}
